home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swags_z.zip / SOUND.SWG / 0059_Play VOC Files without CT-VOICE.DRV.pas < prev    next >
Pascal/Delphi Source File  |  1995-03-03  |  51KB  |  1,202 lines

  1. {
  2.                                   SBDSP
  3.                           Version 1.03 (9/23/94)
  4.                          Written by Ethan Brodsky
  5.           Copyright 1994 by Ethan Brodsky.  All rights reserved.
  6.  
  7. This library is distributed AS IS.  The author specifically disclaims
  8. any responsibility for any loss of profit or any consequential,
  9. incidental, or other damages.  SBDSP is freeware and is distributed
  10. with full Turbo Pascal source code.  You are free to incorporate parts
  11. of the code into your own programs as long as you give credit to Ethan
  12. Brodsky.  This source code may only be distributed in it's original
  13. form, including this documentation file.
  14.  
  15. ------------------------------------------------------------------------
  16.  
  17.     You may have used my SBVox and SBVoice units.  They played VOC files
  18. on a Sound Blaster using Creative Labs' CT-VOICE driver.  Since they
  19. used the CT-VOICE driver, they wouldn't work on other sound cards.  The
  20. driver needed to be included with the program, either in a separate file
  21. or linked into the executable.
  22.  
  23.     SBDSP performs the same functions as the SBVox unit without using
  24. the CT-VOICE driver.  It has only been tested on a SB16 and PAS16, but
  25. it should work on all Sound Blaster compatible sound cards.  By using
  26. DMA transfers, it plays sound without using the CPU, saving processor
  27. cycles for your program.
  28.  
  29.     I have many improvements planned, including 16-bit sound, stereo
  30. effects, and mixing, along with new library for FM music.  But I NEED
  31. FEEDBACK!  If you use my code, tell me about it!  If you make any
  32. modifications, send them to me!  If you have any suggestions for
  33. improvements, tell me about them!  If you want me to write a C version,
  34. or a version to play WAV files, tell me!
  35.  
  36.    You don't need to pay me for using this unit.  All you have to do
  37. is put my name in the credits for your product.  I'd appreciate it if
  38. you'd send me a message telling me how you used SBDSP.  (If you used
  39. it in a game, tell me where I can get it)  And finally, if you ever
  40. have a sound programming job, think of me.
  41.  
  42.     You can find out most of the things you need to know in order to
  43. use this library by looking at the PlayVOC example program, but I'll
  44. go over it again.  The first thing you need to do is to reset the DSP,
  45. initialize SBDSP's internal variables, and install the interrupt
  46. handler.  In order to do this, you need to know the sound cards base
  47. address, IRQ number, and 8-bit DMA channel.   If this is being used
  48. on a Sound Blaster, this information can be obtained from the BLASTER
  49. environment variable.  I don't know whether other cards use this.  You
  50. can use the EnvironmentSet function to find out if the environment
  51. variable is set.  If it is, you can call the function InitSBFromEnv.
  52. Otherwise, you'll have to find out the settings some other way and pass
  53. them to the InitSB function.
  54.  
  55.     Use the LoadVOCFile function to allocate a sound buffer.  Make sure
  56. that you save the value returned from this function.  It is the size of
  57. the allocated buffer.  It will be needed when you deallocate the buffer.
  58. The memory needed for Sound will be allocated inside this function. You
  59. do NOT need to allocate it beforehand.
  60.  
  61.     Before you can play any sounds, you have to turn on the speaker
  62. output.  Do this by calling TurnSpeakerOn.  Make sure you turn it off
  63. at the end of the program.  If you want to install a marker handler,
  64. make sure you do it now by calling SetMarkerProc.  A marker handler
  65. will be called each time a marker block is reached.  Before you install
  66. your marker handler, save the old one using GetMarkerProc.  If the value
  67. returned is not nil, then another marker procedure has been installed.
  68. Call it each time your marker procedure is called.  This is a good
  69. practice to get into when setting up a handler such as this.  It will
  70. make it possible to install more than one marker procedure.
  71.  
  72.     To play a sound, pass a pointer to the sound buffer to PlaySound.
  73. Any sound output in progress will be stopped.  To find out if the sound
  74. is finished, check the SoundPlaying variable.  The VOC file format has
  75. a provision for repeating sounds.  The sound can be set to repeat for
  76. a number of times (Or forever)  You can break out of the loop by calling
  77. BreakLoop.  The current iteration will finish and it will continue to
  78. the next block.  When the program is completely finished playing sound,
  79. call the ShutDownSB procedure.  This will stop any sound output in
  80. progress and remove the interrupt handler.  You should deallocate all
  81. sound buffers by using FreeBuffer.  The pointer to the buffer should be
  82. typecasted as a pointer.  Make sure that you pass the buffer size that
  83. was returned by LoadVOCFile so that the right amount of memory is
  84. deallocated.
  85.  
  86.     This library will not allow you to play 16 bit or stereo VOC files.
  87. It will not work in protected mode since it uses DMA transfers.  If you
  88. have any other questions, feel free to ask.  If you would like me to
  89. make any modifications or a customized version of this unit to use in
  90. your program, contact me and we can work out some arrangements.
  91.  
  92. There are several ways to contact me:
  93.     E-Mail:  ericbrodsky@psl.wisc.edu    (Preferred)
  94.     Phone:   (608) 238-4830
  95.     Mail:
  96.         Ethan Brodsky
  97.       4010 Cherokee Dr.
  98.       Madison, WI 53711
  99.  
  100. Bug fixes and other announcements will be posted in:
  101.     comp.lang.pascal
  102.     comp.sys.ibm.pc.soundcard
  103.     comp.sys.ibm.pc.soundcard.tech
  104.     rec.games.programmer
  105. }
  106.  
  107.  
  108. {       SBDSP is Copyright 1994 by Ethan Brodsky.  All rights reserved.      }
  109. unit Mem;
  110.     interface
  111.         function GetBuffer(var Buffer: pointer; BufferLength: LongInt): boolean;
  112.         procedure FreeBuffer(Buffer: pointer; BufferLength: LongInt);
  113.         function GetAbsoluteAddress(p: pointer): LongInt;
  114.     implementation
  115.         function GetBuffer(var Buffer: pointer; BufferLength: LongInt): boolean;
  116.             var
  117.                 Dummy: pointer;
  118.             begin
  119.                 if MaxAvail < BufferLength
  120.                     then
  121.                         begin
  122.                             GetBuffer := false;
  123.                             Buffer := nil;
  124.                             Exit;
  125.                         end;
  126.                 GetBuffer := true;
  127.                 if BufferLength < $FFFF
  128.                     then
  129.                         GetMem(Buffer, BufferLength)
  130.                     else
  131.                         begin
  132.                             GetMem(Buffer, $FFFF);
  133.                             BufferLength := BufferLength - $FFFF;
  134.                             while BufferLength > $FFFF do
  135.                                 begin
  136.                                     GetMem(Dummy, $FFFF);
  137.                                     BufferLength := BufferLength - $FFFF;
  138.                                 end;
  139.                             GetMem(Dummy, BufferLength);
  140.                         end;
  141.             end;
  142.         procedure FreeBuffer(Buffer: pointer; BufferLength: LongInt);
  143.             var
  144.                 Dummy: pointer;
  145.                 LeftToFree: LongInt;
  146.             begin
  147.                 if BufferLength < $FFFF
  148.                     then
  149.                         FreeMem(Buffer, BufferLength)
  150.                     else
  151.                         begin
  152.                             Dummy := Buffer;
  153.                             LeftToFree := BufferLength;
  154.                             FreeMem(Buffer, $FFFF);
  155.                             LeftToFree := LeftToFree - $FFFF;
  156.                             Dummy := Ptr(Seg(Dummy^) + $1000, Ofs(Dummy^));
  157.                             while LeftToFree > $FFFF do
  158.                                 begin
  159.                                     FreeMem(Dummy, $FFFF);
  160.                                     LeftToFree := LeftToFree - $FFFF;
  161.                                     Dummy := Ptr(Seg(Dummy^) + $1000, Ofs(Dummy^));
  162.                                 end;
  163.                             FreeMem(Dummy, LeftToFree);
  164.                         end;
  165.             end;
  166.         function GetAbsoluteAddress(p: pointer): LongInt;
  167.             begin
  168.                 GetAbsoluteAddress := LongInt(Seg(p^))*16 + LongInt(Ofs(p^));
  169.             end;
  170.     end.
  171.  
  172.  
  173.  
  174.  
  175. {       SBDSP is Copyright 1994 by Ethan Brodsky.  All rights reserved.      }
  176. unit VOC;
  177.     interface
  178.         const
  179.             EndBlockNum            = 0;
  180.             VoiceBlockNum          = 1;
  181.             VoiceContinueBlockNum  = 2;
  182.             SilenceBlockNum        = 3;
  183.             MarkerBlockNum         = 4;
  184.             MessageBlockNum        = 5;
  185.             RepeatBlockNum         = 6;
  186.             RepeatEndBlockNum      = 7;
  187.             ExtendedInfoBlockNum   = 8;
  188.             NewVoiceBlockNum       = 9;
  189.             BlockNames : array[0..9] of string =
  190.                 (
  191.                  'Terminator',
  192.                  'Voice Data',
  193.                  'Voice Continuation',
  194.                  'Silence',
  195.                  'Marker',
  196.                  'Message',
  197.                  'Repeat Loop',
  198.                  'End Repeat Loop',
  199.                  'Extended Info',
  200.                  'New Voice Data'
  201.                 );
  202.             {Used in block type 1 and 8}
  203.             Unpacked8  = 0; {8 bit (Uncompressed)}
  204.             Packed4    = 1; {4 bit}
  205.             Packed26   = 2; {2.6 bit}
  206.             Packed2    = 3; {2 bit}
  207.             PackingNames : array[0..10] of string =
  208.                 (
  209.                  '8 bit unpacked',
  210.                  '4 bit packed',
  211.                  '2.6 bit packed',
  212.                  '2 bit packed',
  213.                  '1 channel multi',
  214.                  '2 channel multi',
  215.                  '3 channel multi',
  216.                  '4 channel multi',
  217.                  '5 channel multi',
  218.                  '6 channel multi',
  219.                  '7 channel multi'
  220.                 );
  221.             {Used in block type 9}
  222.             Uncompressed8     = $0000;
  223.             Compressed4       = $0001;
  224.             Compressed26      = $0002;
  225.             Compressed2       = $0003;
  226.             Uncompressed16    = $0004;
  227.             CompressedALAW    = $0006;
  228.             CompressedMULAW   = $0007;
  229.             CompressedADPCM   = $0200; {Why couldn't they make this $0008?}
  230.             CompressionNames : array[0..7] of string =
  231.                 (
  232.                     '8 bit uncompressed',
  233.                     '4 bit compressed',
  234.                     '2.6 bit compressed',
  235.                     '2 bit compressed',
  236.                     '16 bit uncompressed',
  237.                     '',
  238.                     'ALAW compressed',
  239.                     'MULAW compressed'
  240.                 );
  241.             ExtendedMono   = 0;
  242.             ExtendedStereo = 1;
  243.             ExtendedModeNames : array[0..1] of string = ('Mono', 'Stereo');
  244.  
  245.             NewMono   = 1;      {This is Creative Labs' fault}
  246.             NewStereo = 2;      {Blame it on Creative Labs}
  247.             NewModeNames : array[1..2] of string = ('Mono', 'Stereo');
  248.         type
  249.             PSound = ^TSound;
  250.             TSound = array[0..65520] of byte;
  251.  
  252.             PVOCHeader = ^TVOCHeader;
  253.             TVOCHeader = array[1..26] of byte;
  254.  
  255.             TripleByte = array[1..3] of byte;
  256.  
  257.             PBlock = ^TBlock;
  258.             TBlock =
  259.                 record
  260.                     BlockType: byte;
  261.                     BlockLength: TripleByte;
  262.                 end;
  263.  
  264.             PEndBlock = ^TEndBlock;
  265.             TEndBlock =
  266.                 record
  267.                     BlockType : byte;
  268.                 end;
  269.  
  270.             PVoiceBlock = ^TVoiceBlock;
  271.             TVoiceBlock =
  272.                 record
  273.                     BlockType : byte;
  274.                     BlockLength : TripleByte;
  275.                     SR : byte;
  276.                     Packing : byte;
  277.                     Data : array[0..65520] of byte;
  278.                 end;
  279.  
  280.             PVoiceContinueBlock = ^TVoiceContinueBlock;
  281.             TVoiceContinueBlock =
  282.                 record
  283.                     BlockType : byte;
  284.                     BlockLength : TripleByte;
  285.                     Data : array[0..65520] of byte;
  286.                 end;
  287.  
  288.             PSilenceBlock = ^TSilenceBlock;
  289.             TSilenceBlock =
  290.                 record
  291.                     BlockType : byte;
  292.                     BlockLength : TripleByte;
  293.                     Duration : word;
  294.                     SR : byte;
  295.                 end;
  296.  
  297.             PMarkerBlock = ^TMarkerBlock;
  298.             TMarkerBlock =
  299.                 record
  300.                     BlockType : byte;
  301.                     BlockLength : TripleByte;
  302.                     Marker : word;
  303.                 end;
  304.  
  305.             PMessageBlock = ^TMessageBlock;
  306.             TMessageBlock =
  307.                 record
  308.                     BlockType : byte;
  309.                     BlockLength : TripleByte;
  310.                     Data: array[0..65520] of char;
  311.                 end;
  312.  
  313.             PRepeatBlock = ^TRepeatBlock;
  314.             TRepeatBlock =
  315.                 record
  316.                     BlockType : byte;
  317.                     BlockLength : TripleByte;
  318.                     Count: word;
  319.                 end;
  320.  
  321.             PRepeatEndBlock = ^TRepeatEndBlock;
  322.             TRepeatEndBlock =
  323.                 record
  324.                     BlockType : byte;
  325.                     BlockLength : TripleByte;
  326.                 end;
  327.             PExtendedInfoBlock = ^TExtendedInfoBlock;
  328.             TExtendedInfoBlock =
  329.                 record
  330.                     BlockType : byte;
  331.                     BlockLength : TripleByte;
  332.                     ExtendedSR : word;
  333.                     Packing : byte;
  334.                     Mode : byte; {0 = mono, 1 = stereo}
  335.                 end;
  336.             PNewVoiceBlock = ^TNewVoiceBlock;
  337.             TNewVoiceBlock =
  338.                 record
  339.                     BlockType : byte;
  340.                     BlockLength : TripleByte;
  341.                     SamplingRate : word; {HZ}
  342.                     Dummy1 : array[1..2] of byte;
  343.                     BitsPerSample : byte; {Uncompressed bits per sample}
  344.                     Mode : byte; {1 = mono, 2 = stereo}
  345.                     Compression: word;
  346.                     Dummy2 : array[1..4] of byte;
  347.                     Data : array[0..64000] of byte;
  348.                 end;
  349.         function TripleByteToLongint(TB: TripleByte): LongInt;
  350.         function GetSamplingRate(SR: byte): LongInt;
  351.         function GetSRByte(SamplingRate: word): byte;
  352.         function GetExtendedSamplingRate(ExtendedSR: word; Mode: byte): LongInt;
  353.         function BlockSize(Block: PBlock): LongInt;
  354.         procedure IncrementPtr(var P: pointer; Count: word);
  355.         function FindNextBlock(Block: PBlock): PBlock;
  356.         function LoadVOCFile(FileName: string; var Sound: PSound): LongInt;
  357.     implementation
  358.         uses
  359.             Mem;
  360.         function TripleByteToLongint(TB: TripleByte): LongInt;
  361.             begin
  362.                 TripleByteToLongint := LongInt(TB[1]) + LongInt(TB[2]) SHL 8 + LongInt(TB[3]) SHL 16;
  363.             end;
  364.         function GetSamplingRate(SR: byte): LongInt;
  365.             begin
  366.                 GetSamplingRate := -1000000 div (SR - 256);
  367.             end;
  368.         function GetSRByte(SamplingRate: word): byte;
  369.             begin
  370.                 GetSRByte := 256-(1000000 div SamplingRate);
  371.             end;
  372.         function GetExtendedSamplingRate(ExtendedSR: word; Mode: byte): LongInt;
  373.             begin
  374.                 case Mode
  375.                     of
  376.                         ExtendedMono:
  377.                             GetExtendedSamplingRate := -256000000 div (ExtendedSR-65536);
  378.                         ExtendedStereo:
  379.                             GetExtendedSamplingRate := (-256000000 div (ExtendedSR-65536)) div 2;
  380.                     end;
  381.             end;
  382.         function BlockSize(Block: PBlock): LongInt;
  383.             begin
  384.                 BlockSize := TripleByteToLongInt(Block^.BlockLength) + 4;
  385.             end;
  386.         procedure IncrementPtr(var P: pointer; Count: word);
  387.           {Easier to implement in assembly}
  388.             begin
  389.                 asm
  390.                     LES  DI, P
  391.                     MOV  BX, Count
  392.                     MOV  AX, ES:[DI]
  393.                     MOV  DX, ES:[DI+2]
  394.                     ADD  AX, BX
  395.                     CMP  AX, $000F
  396.                     JNA  @1
  397.                     MOV  BX, AX
  398.                     AND  AX, $F
  399.                     AND  BX, $FFF0
  400.                     MOV  CL, 4
  401.                     SHR  BX, CL
  402.                     ADD  DX, BX
  403.                   @1:
  404.                     MOV  ES:[DI], AX
  405.                     MOV  ES:[DI+2], DX
  406.                 end;
  407.             end;
  408.         function FindNextBlock(Block: PBlock): PBlock;
  409.             var
  410.                 NewBlock: PBlock;
  411.                 BlockSize: LongInt;
  412.             begin
  413.                 if Block^.BlockType = EndBlockNum
  414.                     then
  415.                         begin
  416.                             FindNextBlock := nil;
  417.                             Exit;
  418.                         end;
  419.                 NewBlock := Block;
  420.                 BlockSize := TripleByteToLongInt(Block^.BlockLength) + 4;
  421.                 while BlockSize > 0 do
  422.                     begin
  423.                         if BlockSize > 64000
  424.                             then
  425.                                 begin
  426.                                     IncrementPtr(pointer(NewBlock), 64000);
  427.                                     Dec(BlockSize, 64000);
  428.                                 end
  429.                             else
  430.                                 begin
  431.                                     IncrementPtr(pointer(NewBlock), BlockSize);
  432.                                     BlockSize := 0;
  433.                                 end;
  434.                     end;
  435.                 FindNextBlock := NewBlock;
  436.             end;
  437.         function LoadVOCFile(FileName: string; var Sound: PSound): LongInt;
  438.            var
  439.                 f: file;
  440.                 Dummy: Pointer;
  441.                 LeftToRead: LongInt;
  442.                 Header: PVOCHeader;
  443.             begin
  444.                 Assign(f, FileName);
  445.                 {$I-}
  446.                 Reset(f, 1);
  447.                 {$I+}
  448.                 if IOResult <> 0
  449.                     then
  450.                         begin
  451.                             LoadVOCFile := 0; {Couldn't open file}
  452.                             Exit;
  453.                         end;
  454.                 LeftToRead := FileSize(f) - SizeOf(Header^);
  455.                 LoadVOCFile := LeftToRead;
  456.                 New(Header);
  457.                 BlockRead(f, Header^, SizeOf(Header^));
  458.  
  459.                 if GetBuffer(pointer(Sound), LeftToRead) <> true
  460.                     then
  461.                         begin
  462.                             LoadVOCfile := 0; {Failed to allocate memory}
  463.                             Exit;
  464.                         end;
  465.                 Dummy := Sound;
  466.                 while LeftToRead > 0 do
  467.                     begin
  468.                         if LeftToRead < 64000
  469.                             then
  470.                                 begin
  471.                                     BlockRead(f, Dummy^, LeftToRead);
  472.                                     LeftToRead := 0;
  473.                                 end
  474.                             else
  475.                                 begin
  476.                                     BlockRead(f, Dummy^, 64000);
  477.                                     LeftToRead := LeftToRead - 64000;
  478.                                     IncrementPtr(Dummy, 64000);
  479.                                 end;
  480.                     end;
  481.                 Close(f);
  482.                 Dispose(Header);
  483.             end;
  484.     begin
  485.     end.
  486.  
  487.  
  488.  
  489. {       SBDSP is Copyright 1994 by Ethan Brodsky.  All rights reserved.      }
  490.  
  491. {$X+} {Extended syntax on}
  492. unit SBDSP;
  493.     interface
  494.         uses
  495.             VOC;
  496.         const
  497.             On = true;
  498.             Off = false;
  499.         type
  500.             Proc = procedure;
  501.         function InitSB(IRQ: byte; BaseIO: word; DMAChannel: byte): boolean;
  502.           {This function must be called before any sound is played.  It will }
  503.           {initialize internal variables, reset the DSP chip, and install the}
  504.           {interrupt handler.                                                }
  505.           {IRQ:           The sound card's IRQ setting (Usually 5 or 7)      }
  506.           {BaseIO:        The sound card's base IO address (Usually $220)    }
  507.           {DMAChannel:    The sound card's 8-bit DMA channel (Usually 1)     }
  508.           {Returns:                                                          }
  509.           {    TRUE:      Sound card initialized correctly                   }
  510.           {    FALSE:     Error initializing sound card                      }
  511.         function EnvironmentSet: boolean;
  512.           {Returns:                                                          }
  513.           {    TRUE:  The BLASTER environment variable is set                }
  514.           {    FALSE: The BLASTER environment variable isn't set             }
  515.         function InitSBFromEnv: boolean;
  516.           {This function initializes the sound card from the settings stored }
  517.           {in the BLASTER environment variable.  I'm not sure if all sound   }
  518.           {cards use the enviroment variable.                                }
  519.           {Returns:                                                          }
  520.           {    TRUE:  Environment variable found and sound card initialized  }
  521.           {    FALSE: Environment variable not set or error initializing card}
  522.         procedure ShutDownSB;
  523.           {This procedure must be called at the end of the program.  It stops}
  524.           {sound output, removes the interrupt handler, and restores the old }
  525.           {interrupt handler.                                                }
  526.         procedure InstallHandler;
  527.           {This procedure will reinstall the }
  528.         procedure UninstallHandler;
  529.           {This procedure will remove the interrupt handler.  You should not }
  530.           {need to call this.  If you do, sound output won't work until the  }
  531.           {handler is reinstalled.                                           }
  532.         function ResetDSP: boolean;
  533.           {This function resets the sound card's DSP chip.                   }
  534.           {Returns:                                                          }
  535.           {    TRUE:    The sound card's DSP chip was successfully reseted   }
  536.           {    FALSE:   The chip couldn't be initialized (Don't use it)      }
  537.         function GetDSPVersion: string;
  538.           {This function returns a string containing the DSP chip version.   }
  539.         procedure TurnSpeakerOn;
  540.           {This procedure turns on the speaker.  This should be called before}
  541.           {a sound is played, but after the sound card is initialized.       }
  542.         procedure TurnSpeakerOff;
  543.           {Turn off the speaker so that sound can't be heard.  You should do }
  544.           {this when your program is finished playing sound.                 }
  545.         function GetSpeakerState: boolean;
  546.           {Returns the state of the speaker.  Only works on SBPro and higher.}
  547.           {Returns:                                                          }
  548.           {    TRUE:    Speaker is on                                        }
  549.           {    FALSE:   Speaker is off                                       }
  550.         procedure PlaySound(Sound: PSound);
  551.           {Stops any sound in progress and start playing the sound specified.}
  552.           {Sound:       Pointer to buffer that the VOC file was loaded into  }
  553.         procedure PauseSound;
  554.           {Pauses the sound output in progress.                              }
  555.         procedure ContinueSound;
  556.           {Continues sound output stopped by Pause.                          }
  557.         procedure BreakLoop;
  558.           {Stops the loop at the end of the current iteration and continues  }
  559.           {with the next block.                                              }
  560.         procedure SetMarkerProc(MarkerProcedure: pointer);
  561.           {Installs a marker handler.  Each time a marker block is reached,  }
  562.           {the procedure specified is called.  Before installing a handler,  }
  563.           {you should store the old handler.  Your handler should also call  }
  564.           {the old handler.  Look in the example program to see how this is  }
  565.           {done.                                                             }
  566.           {MarkerProcedure:  Pointer to the marker procedure                 }
  567.         procedure GetMarkerProc(var MarkerProcedure: pointer);
  568.           {Gets the current marker procedure.                                }
  569.           {MarkerProcedure:  Current marker procedure (nil if none)          }
  570.         var
  571.             SoundPlaying  : boolean;
  572.             Looping       : boolean;
  573.             UnknownBlock  : boolean;
  574.             UnplayedBlock : boolean;
  575.             LastMarker    : word;
  576.     implementation
  577.         uses
  578.             DOS,
  579.             CRT,
  580.             Mem;
  581.         const
  582.             {DSP Commands}
  583.             CmdDirectDAC       = $10;
  584.             CmdNormalDMADAC    = $14;
  585.             Cmd2BitDMADAC      = $16;
  586.             Cmd2BitRefDMADAC   = $17;
  587.             CmdDirectADC       = $20;
  588.             CmdNormalDMAADC    = $24;
  589.             CmdSetTimeConst    = $40;
  590.             CmdSetBlockSize    = $48;
  591.             Cmd4BitDMADAC      = $74;
  592.             Cmd4BitRefDMADAC   = $75;
  593.             Cmd26BitDMADAC     = $76;
  594.             Cmd26BitRefDMADAC  = $77;
  595.             CmdSilenceBlock    = $80;
  596.             CmdHighSpeedDMADAC = $91;
  597.             CmdHighSpeedDMAADC = $99;
  598.             CmdHaltDMA         = $D0;
  599.             CmdSpeakerOn       = $D1;
  600.             CmdSpeakerOff      = $D3;
  601.             CmdGetSpeakerState = $D8;
  602.             CmdContinueDMA     = $D4;
  603.             CmdGetVersion      = $E1;
  604.             DACCommands : array[0..3] of byte = (CmdNormalDMADAC, Cmd4BitDMADAC, Cmd26BitDMADAC, Cmd2BitDMADAC);
  605.         var
  606.             ResetPort    : word;
  607.             ReadPort     : word;
  608.             WritePort    : word;
  609.             PollPort     : word;
  610.  
  611.             PICPort      : byte;
  612.             IRQStartMask : byte;
  613.             IRQStopMask  : byte;
  614.             IRQIntVector : byte;
  615.             IRQHandlerInstalled : boolean;
  616.  
  617.             DMAStartMask : byte;
  618.             DMAStopMask  : byte;
  619.             DMAModeReg   : byte;
  620.  
  621.             OldIntVector : pointer;
  622.             OldExitProc  : pointer;
  623.  
  624.             MarkerProc   : pointer;
  625.         var
  626.             VoiceStart     : LongInt;
  627.             CurPos         : LongInt;
  628.             CurPageEnd     : LongInt;
  629.             VoiceEnd       : LongInt;
  630.             LeftToPlay     : LongInt;
  631.             TimeConstant   : byte;
  632.             SoundPacking   : byte;
  633.             CurDACCommand  : byte;
  634.  
  635.             LoopStart      : PBlock;
  636.             LoopsRemaining : word;
  637.             EndlessLoop    : boolean;
  638.  
  639.             SilenceBlock   : boolean;
  640.  
  641.             CurBlock       : PBlock;
  642.             NextBlock      : PBlock;
  643.  
  644.         procedure EnableInterrupts;  InLine($FB); {STI}
  645.         procedure DisableInterrupts; InLine($FA); {CLI}
  646.         procedure WriteDSP(Value: byte);
  647.             Inline
  648.               (
  649.                 $8B/$16/>WritePort/    {MOV   DX, WritePort (Variable)  }
  650.                 $EC/                   {IN    AL, DX                    }
  651.                 $24/$80/               {AND   AL, 80h                   }
  652.                 $75/$FB/               {JNZ   -05                       }
  653.                 $58/                   {POP   AX                        }
  654.                 $8B/$16/>WritePort/    {MOV   DX, WritePort (Variable)  }
  655.                 $EE                    {OUT   DX, AL                    }
  656.               );
  657.         function ReadDSP: byte;
  658.             Inline
  659.               (
  660.                 $8B/$16/>PollPort/     {MOV   AL, PollPort  (Variable)  }
  661.                 $EC/                   {IN    AL, DX                    }
  662.                 $24/$80/               {AND   AL, 80h                   }
  663.                 $74/$FB/               {JZ    -05                       }
  664.                 $8B/$16/>ReadPort/     {MOV   DX, ReadPort  (Variable)  }
  665.                 $EC                    {IN    AL,DX                     }
  666.               );
  667.         function InitSB(IRQ: byte; BaseIO: word; DMAChannel: byte): boolean;
  668.             const
  669.                 IRQIntNums : array[0..15] of byte =
  670.                     ($08, $09, $0A, $0B, $0C, $0D, $0E, $0F,
  671.                      $70, $71, $72, $73, $74, $75, $76, $77);
  672.             var
  673.                 Success: boolean;
  674.             begin
  675.                 if IRQ <= 7
  676.                     then PICPort := $21   {INTC1}
  677.                     else PICPort := $A1;  {INTC2}
  678.                 IRQIntVector := IRQIntNums[IRQ];
  679.                 IRQStopMask  := 1 SHL (IRQ mod 8);
  680.                 IRQStartMask := not(IRQStopMask);
  681.  
  682.                 ResetPort := BaseIO + $6;
  683.                 ReadPort  := BaseIO + $A;
  684.                 WritePort := BaseIO + $C;
  685.                 PollPort  := BaseIO + $E;
  686.  
  687.                 DMAStartMask := DMAChannel + $00; {000000xx}
  688.                 DMAStopMask  := DMAChannel + $04; {000001xx}
  689.                 DMAModeReg   := DMAChannel + $48; {010010xx}
  690.  
  691.                 Success := ResetDSP;
  692.                 if Success then InstallHandler;
  693.                 InitSB := Success;
  694.             end;
  695.         function EnvironmentSet: boolean;
  696.             begin
  697.                 EnvironmentSet := GetEnv('BLASTER') <> '';
  698.             end;
  699.         function GetSetting(BLASTER: string; Letter: char; Hex: boolean; var Value: word): boolean;
  700.             var
  701.                 EnvStr: string;
  702.                 NumStr: string;
  703.                 ErrorCode: integer;
  704.             begin
  705.                 EnvStr := BLASTER + ' ';
  706.                 Delete(EnvStr, 1, Pos(Letter, EnvStr));
  707.                 NumStr := Copy(EnvStr, 1, Pos(' ', EnvStr)-1);
  708.                 if Hex
  709.                     then Val('$' + NumStr, Value, ErrorCode)
  710.                     else Val(NumStr, Value, ErrorCode);
  711.                 if ErrorCode <> 0
  712.                     then GetSetting := false
  713.                     else GetSetting := true;
  714.             end;
  715.         function GetSettings(var BaseIO, IRQ, DMAChannel: word): boolean;
  716.             var
  717.                 EnvStr: string;
  718.                 i: byte;
  719.             begin
  720.                 EnvStr := GetEnv('BLASTER');
  721.                 for i := 1 to Length(EnvStr) do EnvStr[i] := UpCase(EnvStr[i]);
  722.                 GetSettings := true;
  723.                 if EnvStr = ''
  724.                     then
  725.                         GetSettings := false
  726.                     else
  727.                         begin
  728.                             if not(GetSetting(EnvStr, 'A', true, BaseIO))
  729.                                 then GetSettings := false;
  730.                             if not(GetSetting(EnvStr, 'I', false, IRQ))
  731.                                 then GetSettings := false;
  732.                             if not(GetSetting(EnvStr, 'D', false, DMAChannel))
  733.                                 then GetSettings := false;
  734.                         end;
  735.             end;
  736.         function InitSBFromEnv: boolean;
  737.             var
  738.                 IRQ, BaseIO, DMAChannel: word;
  739.             begin
  740.                 if GetSettings(BaseIO, IRQ, DMAChannel)
  741.                     then InitSBFromEnv := InitSB(IRQ, BaseIO, DMAChannel)
  742.                     else InitSBFromEnv := false;
  743.             end;
  744.         procedure ShutDownSB;
  745.             begin
  746.                 ResetDSP;
  747.                 UninstallHandler;
  748.             end;
  749.         function ResetDSP: boolean;
  750.             var
  751.                 i: byte;
  752.             begin
  753.                 Port[ResetPort] := 1;
  754.                 Delay(1);
  755.                 Port[ResetPort] := 0;
  756.                 i := 1;
  757.                 while (ReadDSP <> $AA) and (i < 100) do
  758.                     Inc(i);
  759.                 if i < 100
  760.                     then ResetDSP := true
  761.                     else ResetDSP := false;
  762.             end;
  763.         function GetDSPVersion: string;
  764.             var
  765.                 MajorByte, MinorByte: byte;
  766.                 MajorStr, MinorStr: string;
  767.             begin
  768.                 WriteDSP(CmdGetVersion);
  769.                 MajorByte := ReadDSP;   Str(MajorByte, MajorStr);
  770.                 MinorByte := ReadDSP;   Str(MinorByte, MinorStr);
  771.                 GetDSPVersion := MajorStr + '.'  + MinorStr;
  772.             end;
  773.         procedure TurnSpeakerOn;
  774.             begin
  775.                 WriteDSP(CmdSpeakerOn);
  776.             end;
  777.         procedure TurnSpeakerOff;
  778.             begin
  779.                 WriteDSP(CmdSpeakerOff);
  780.             end;
  781.         function GetSpeakerState: boolean;
  782.             var
  783.                 SpeakerByte: byte;
  784.             begin
  785.                 WriteDSP(CmdGetSpeakerState);
  786.                 SpeakerByte := ReadDSP;
  787.                 if SpeakerByte = 0
  788.                     then GetSpeakerState := Off
  789.                     else GetSpeakerState := On;
  790.             end;
  791.         procedure StartDMADSP;
  792.             var
  793.                 Page: byte;
  794.                 Offset: word;
  795.                 Length: word;
  796.                 NextPageStart: LongInt;
  797.             begin
  798.                 Page := CurPos shr 16;
  799.                 Offset := CurPos mod 65536;
  800.                 if VoiceEnd < CurPageEnd
  801.                     then Length := LeftToPlay-1
  802.                     else Length := CurPageEnd - CurPos;
  803.  
  804.                 Inc(CurPos, LongInt(Length)+1);
  805.                 Dec(LeftToPlay, LongInt(Length)+1);
  806.                 Inc(CurPageEnd, 65536);
  807.  
  808.                 WriteDSP(CmdSetTimeConst);
  809.                 WriteDSP(TimeConstant);
  810.                 Port[$0A] := DMAStopMask;
  811.                 Port[$0C] := $00;
  812.                 Port[$0B] := DMAModeReg;
  813.                 Port[$02] := Lo(Offset);
  814.                 Port[$02] := Hi(Offset);
  815.                 Port[$03] := Lo(Length);
  816.                 Port[$03] := Hi(Length);
  817.                 Port[$83] := Page;
  818.                 Port[$0A] := DMAStartMask;
  819.                 WriteDSP(CurDACCommand);
  820.                 WriteDSP(Lo(Length));
  821.                 WriteDSP(Hi(Length));
  822.             end;
  823.         procedure CallMarkerProc;
  824.             begin
  825.                 if MarkerProc <> nil then Proc(MarkerProc);
  826.             end;
  827.         function HandleBlock(Block: PBlock): boolean;
  828.             begin
  829.                 HandleBlock := false;
  830.                 case Block^.BlockType
  831.                     of
  832.                         EndBlockNum:
  833.                             begin
  834.                                 SoundPlaying := false;
  835.                                 HandleBlock := true;
  836.                             end;
  837.                         VoiceBlockNum:
  838.                             begin
  839.                                 VoiceStart := GetAbsoluteAddress(Block) + 6;
  840.                                 CurPageEnd := ((VoiceStart shr 16) shl 16) + 65536 - 1;
  841.                                 LeftToPlay := BlockSize(Block) - 6;
  842.                                 VoiceEnd := VoiceStart + LeftToPlay;
  843.                                 CurPos := VoiceStart;
  844.                                 TimeConstant := PVoiceBlock(Block)^.SR;
  845.                                 SoundPacking := PVoiceBlock(Block)^.Packing;
  846.                                 CurDACCommand := DACCommands[SoundPacking];
  847.                                 StartDMADSP;
  848.                                 HandleBlock := true;
  849.                             end;
  850.                         VoiceContinueBlockNum:
  851.                             begin
  852.                                 VoiceStart := GetAbsoluteAddress(Block)+4;
  853.                                 LeftToPlay := BlockSize(Block) - 4;
  854.                                 VoiceEnd := VoiceStart + LeftToPlay;
  855.                                 CurPos := VoiceStart;
  856.                                 StartDMADSP;
  857.                                 HandleBlock := true;
  858.                             end;
  859.                         SilenceBlockNum:
  860.                              begin
  861.                                  SilenceBlock := true;
  862.                                  WriteDSP(CmdSetTimeConst);
  863.                                  WriteDSP(PSilenceBlock(Block)^.SR);
  864.                                  WriteDSP(CmdSilenceBlock);
  865.                                  WriteDSP(Lo(PSilenceBlock(Block)^.Duration+1));
  866.                                  WriteDSP(Hi(PSilenceBlock(Block)^.Duration+1));
  867.                                  HandleBlock := true;
  868.                              end;
  869.                         MarkerBlockNum:
  870.                              begin
  871.                                  LastMarker := PMarkerBlock(Block)^.Marker;
  872.                                  CallMarkerProc;
  873.                              end;
  874.                         MessageBlockNum:
  875.                             begin
  876.                             end;
  877.                         RepeatBlockNum:
  878.                             begin
  879.                                  LoopStart := NextBlock;
  880.                                  LoopsRemaining := PRepeatBlock(Block)^.Count+1;
  881.                                  if LoopsRemaining = 0 {Wrapped around from $FFFF}
  882.                                      then EndlessLoop := true
  883.                                      else EndlessLoop := false;
  884.                                  Looping := true;
  885.                              end;
  886.                         RepeatEndBlockNum:
  887.                              begin
  888.                                  if not(EndlessLoop)
  889.                                      then
  890.                                          begin
  891.                                              Dec(LoopsRemaining);
  892.                                              if LoopsRemaining = 0
  893.                                                  then
  894.                                                      begin
  895.                                                          Looping := false;
  896.                                                          Exit;
  897.                                                      end;
  898.                                          end;
  899.                                  NextBlock := LoopStart;
  900.                              end;
  901.                         NewVoiceBlockNum:
  902.                              begin
  903.                                  if (PNewVoiceBlock(Block)^.Mode = NewStereo) or (PNewVoiceBlock(Block)^.BitsPerSample = 16)
  904.                                      then
  905.                                          UnplayedBlock := true
  906.                                      else
  907.                                          begin
  908.                                              VoiceStart := GetAbsoluteAddress(Block) + 16;
  909.                                              CurPageEnd := ((VoiceStart shr 16) shl 16) + 65536 - 1;
  910.                                              LeftToPlay := BlockSize(Block) - 16;
  911.                                              VoiceEnd := VoiceStart + LeftToPlay;
  912.                                              CurPos := VoiceStart;
  913.                                              TimeConstant := GetSRByte(PNewVoiceBlock(Block)^.SamplingRate);
  914.                                              SoundPacking := PNewVoiceBlock(Block)^.Compression;
  915.                                              CurDACCommand := DACCommands[SoundPacking];
  916.                                              StartDMADSP;
  917.                                              HandleBlock := true;
  918.                                          end;
  919.                              end;
  920.                         else
  921.                              UnknownBlock := true;
  922.                     end;
  923.             end;
  924.         procedure ProcessBlocks;
  925.             begin
  926.                 repeat
  927.                     CurBlock := NextBlock;
  928.                     NextBlock := FindNextBlock(pointer(CurBlock));
  929.                 until HandleBlock(CurBlock);
  930.             end;
  931.         procedure ClearInterrupt;
  932.             var
  933.                 Temp: byte;
  934.             begin
  935.                 Temp := Port[PollPort];
  936.                 Port[$20] := $20;
  937.             end;
  938.         procedure IntHandler; interrupt;
  939.             begin
  940.                 if SilenceBlock {Interrupted because a silence block ended}
  941.                     then
  942.                         begin
  943.                             SilenceBlock := false;
  944.                             ProcessBlocks;
  945.                         end
  946.                     else {Interrupted because a DMA transfer was completed}
  947.                         if LeftToPlay <> 0
  948.                             then StartDMADSP
  949.                             else ProcessBlocks;
  950.  
  951.                 ClearInterrupt;
  952.             end;
  953.         procedure PlaySound(Sound: PSound);
  954.             begin
  955.                 PauseSound;
  956.                 NextBlock      := PBlock(Sound);
  957.                 SoundPlaying   := true;
  958.                 Looping        := false;
  959.                 LastMarker     := 0;
  960.                 UnknownBlock   := false;
  961.                 UnplayedBlock  := false;
  962.  
  963.                 LoopStart      := nil;
  964.                 LoopsRemaining := 0;
  965.                 EndlessLoop    := false;
  966.  
  967.                 ProcessBlocks;
  968.             end;
  969.         procedure PauseSound;
  970.             begin
  971.                 WriteDSP(CmdHaltDMA);
  972.             end;
  973.         procedure ContinueSound;
  974.             begin
  975.                 WriteDSP(CmdContinueDMA);
  976.             end;
  977.         procedure BreakLoop;
  978.             begin
  979.                 LoopsRemaining := 1;
  980.                 EndlessLoop := false;
  981.             end;
  982.  
  983.         procedure StopSBIRQ;
  984.             begin
  985.                 Port[PICPort] := Port[PICPort] OR IRQStopMask;
  986.             end;
  987.         procedure StartSBIRQ;
  988.             begin
  989.                 Port[PICPort] := Port[PICPort] AND IRQStartMask;
  990.             end;
  991.         procedure InstallHandler;
  992.             begin
  993.                 DisableInterrupts;
  994.                 StopSBIRQ;
  995.                 GetIntVec(IRQIntVector, OldIntVector);
  996.                 SetIntVec(IRQIntVector, @IntHandler);
  997.                 StartSBIRQ;
  998.                 EnableInterrupts;
  999.                 IRQHandlerInstalled := true;
  1000.             end;
  1001.         procedure UninstallHandler;
  1002.             begin
  1003.                 DisableInterrupts;
  1004.                 StopSBIRQ;
  1005.                 SetIntVec(IRQIntVector, OldIntVector);
  1006.                 EnableInterrupts;
  1007.                 IRQHandlerInstalled := false;
  1008.             end;
  1009.  
  1010.         procedure SetMarkerProc(MarkerProcedure: pointer);
  1011.             begin
  1012.                 MarkerProc := MarkerProcedure;
  1013.             end;
  1014.         procedure GetMarkerProc(var MarkerProcedure: pointer);
  1015.             begin
  1016.                 MarkerProcedure := MarkerProc;
  1017.             end;
  1018.         procedure SBDSPExitProc; far;
  1019.             begin
  1020.                 ExitProc := OldExitProc;
  1021.                 ResetDSP;
  1022.                 if (IRQHandlerInstalled = true) then UninstallHandler;
  1023.             end;
  1024.     begin
  1025.         MarkerProc   := nil;
  1026.         OldExitProc  := ExitProc;
  1027.         ExitProc     := @SBDSPExitProc;
  1028.         SoundPlaying := false;
  1029.     end.
  1030.  
  1031.  
  1032. {       SBDSP is Copyright 1994 by Ethan Brodsky.  All rights reserved.      }
  1033. {$M 16384, 0, 419430   Give some memory to the DOS shell.  If you are not}
  1034. {going to shell to DOS, you can remove this line and let your program use}
  1035. {all available memory for the heap.}
  1036. program PlayVOCDirect;
  1037.     uses
  1038.         CRT,
  1039.         DOS,
  1040.         Mem,
  1041.         SBDSP,
  1042.         VOC;
  1043.     const
  1044.         IRQ        = 5;
  1045.         BaseIO     = $220;
  1046.         DMAChannel = 1;
  1047.         DefaultVOC = 'C:\MUSIC\ESCAPE2.VOC';
  1048.          {Put the name of the VOC file to play here}
  1049.          {or pass it as a parameter to the program.}
  1050.     var
  1051.         VOCFileName : string;
  1052.         SoundSize   : LongInt;
  1053.         Sound       : PSound;
  1054.         Chr         : char;
  1055.         OldMarkerProc : pointer;
  1056.     function GetHexWordStr(w: word): string;
  1057.         const
  1058.             HexChars: array [0..$F] of Char = '0123456789ABCDEF';
  1059.         begin
  1060.             GetHexWordStr := HexChars[Hi(w) shr 4] + HexChars[Hi(w) and $F] +
  1061.                              HexChars[Lo(w) shr 4] + HexChars[Lo(w) and $F];
  1062.         end;
  1063.     procedure DisplayMarker; far;
  1064.         var
  1065.             Hour, Minute, Second, Sec100: word;
  1066.         begin
  1067.             GetTime(Hour, Minute, Second, Sec100);
  1068.             writeln('Reached marker ', LastMarker,
  1069.                     ' at ', Hour, ':', Minute, ':', Second, '.', Sec100);
  1070.             if (OldMarkerProc <> nil) then Proc(OldMarkerProc);
  1071.               {If another handler is installed, call it}
  1072.         end;
  1073.     procedure WriteInstructions;
  1074.         begin
  1075.             writeln('Begining output of sound file');
  1076.             writeln('Press <B> to break loop');
  1077.             writeln('Press <P> to pause output');
  1078.             writeln('Press <C> to continue output');
  1079.             writeln('Press <D> to shell to DOS');
  1080.             writeln('Press <X> to stop output and exit');
  1081.         end;
  1082.     begin
  1083.         writeln; writeln;
  1084.  
  1085.         if EnvironmentSet
  1086.             then
  1087.                 begin
  1088.                     if InitSBFromEnv
  1089.                         then
  1090.                             begin
  1091.                                 writeln('Sound card initialized correctly using the BLASTER environment variable!');
  1092.                                 writeln('DSP version ', GetDSPVersion);
  1093.                             end
  1094.                         else
  1095.                             begin
  1096.                                 writeln('Error initializing sound card!');
  1097.                                 Halt(255);
  1098.                             end;
  1099.                 end
  1100.             else
  1101.                 begin
  1102.                     writeln('BLASTER environment variable not set, using default settings');
  1103.                     writeln('IRQ = ', IRQ, '    Base IO = $', GetHexWordStr(BaseIO), '    DMA Channel = ', DMAChannel );
  1104.                     if InitSB(IRQ, BaseIO, DMAChannel)
  1105.                         then
  1106.                             begin
  1107.                                 writeln('Sound card initialized correctly!');
  1108.                                 writeln('DSP version ', GetDSPVersion);
  1109.                             end
  1110.                         else
  1111.                             begin
  1112.                                 writeln('Error initializing sound card!');
  1113.                                 Halt(255);
  1114.                             end;
  1115.                 end;
  1116.  
  1117.         if ParamCount = 0
  1118.             then VOCFileName := DefaultVOC
  1119.             else VOCFileName := ParamStr(1);
  1120.         SoundSize := LoadVOCfile(VOCFileName, Sound);  writeln('Sound file loaded');
  1121.         if SoundSize = 0
  1122.             then
  1123.                 begin
  1124.                     writeln('Error loading VOC file.  Probably because:');
  1125.                     writeln('    1.  There is no VOC file by name ', VOCFileName, '.');
  1126.                     writeln('    2.  There is not enough memory to load it.');
  1127.                     writeln('        Largest available block:  ', MaxAvail, ' bytes');
  1128.                     Halt;
  1129.                 end;
  1130.  
  1131.         GetMarkerProc(OldMarkerProc);
  1132.         SetMarkerProc(@DisplayMarker);
  1133.  
  1134.         TurnSpeakerOn;
  1135.         WriteInstructions;
  1136.         PlaySound(Sound);
  1137.         repeat
  1138.             if KeyPressed
  1139.                 then
  1140.                     begin
  1141.                         Chr := UpCase(ReadKey);
  1142.                         case Chr
  1143.                             of
  1144.                                 'B':
  1145.                                     begin
  1146.                                         BreakLoop;
  1147.                                         writeln('Broke out of loop');
  1148.                                     end;
  1149.  
  1150.                                 'P':
  1151.                                     begin
  1152.                                         PauseSound;
  1153.                                         writeln('Sound output paused');
  1154.                                     end;
  1155.                                 'C':
  1156.                                     begin
  1157.                                         ContinueSound;
  1158.                                         writeln('Sound output continued');
  1159.                                     end;
  1160.                                 'D':
  1161.                                     begin
  1162.                                         SwapVectors;
  1163.                                         Exec(GetEnv('COMSPEC'), '');
  1164.                                         if DOSError <> 0
  1165.                                             then
  1166.                                                 begin
  1167.                                                     writeln('Error running COMMAND.COM!');
  1168.                                                     Halt(255);
  1169.                                                 end;
  1170.                                         SwapVectors;
  1171.                                         WriteInstructions;
  1172.                                     end;
  1173.                                 'X':
  1174.                                     begin
  1175.                                         PauseSound;
  1176.                                         writeln('Sound output stopped!');
  1177.                                         Exit;
  1178.                                     end;
  1179.                             end;
  1180.                     end;
  1181.             if UnknownBlock
  1182.                 then
  1183.                     begin
  1184.                         writeln('An unknown VOC block was reached.  It is probably');
  1185.                         writeln('block 8, which I didn''t implement because it is');
  1186.                         writeln('useless. (At least for this library it is)');
  1187.                         UnknownBlock := false;
  1188.                     end;
  1189.             if UnplayedBlock
  1190.                then
  1191.                    begin
  1192.                        writeln('A 16-bit or stereo block was reached.  This library');
  1193.                        writeln('doesn''t support either of these.');
  1194.                        UnplayedBlock := false;
  1195.                    end;
  1196.         until (SoundPlaying = false);
  1197.         TurnSpeakerOff;
  1198.  
  1199.         SetMarkerProc(OldMarkerProc); {Not really necessary}
  1200.         FreeBuffer(pointer(Sound), SoundSize);
  1201.         ShutDownSB;
  1202.     end.